#include <clang-c/Index.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "lccc.h"

typedef struct CXUnsavedFile CXUnsavedFile;
typedef struct LCContext{
    CXTranslationUnit TU;
    CXIndex index;
    char* file;
    int alloc_arg;
    int argc;
    char** argv;
    int alloc_unsaved_files;
    int unsaved_files_num;
    struct CXUnsavedFile *unsaved_files;
    CXCodeCompleteResults *last_result;

    //匹配文本,默认为0
    //默认是匹配kind = 1
    int matchlen;        //一般为0,
    char*match; //

    //处理翻译单元的优化开关
    int tuopt;

}LCContext;

typedef struct{
    int len;
    int allocSize;
    char **list;
}LCCStringList;

LCContext* LCC_init(){
    LCContext*c = calloc(1,sizeof(*c));
    return c;
}

void LCC_free(LCContext*c){
    if(c){
        if(c->file)
            free(c->file);
        if(c->argv)
            free(c->argv);
        if(c->TU){
            clang_disposeTranslationUnit(c->TU);
        }
        if(c->index){
            clang_disposeIndex(c->index);
        }
        if(c->last_result){
            clang_disposeCodeCompleteResults(c->last_result);
        }
        if(c->unsaved_files){
            for(int i=0;i<c->unsaved_files_num;i++){
                if(c->unsaved_files[i].Filename)
                    free((char*)c->unsaved_files[i].Filename);
                if(c->unsaved_files[i].Contents)
                    free((char*)c->unsaved_files[i].Contents);

            }
        }
        if(c->match){
            free((char*)c->match);
        }
        memset(c,0,sizeof(*c));
        free(c);
    }
}

int LCC_set_match(LCContext*c,const char*m){
    if(c){
        if(c->match){
            free(c->match);
            c->match = 0;
        }
        c->matchlen = strlen(m);
        if(c->matchlen>0){
            c->match = strdup(m);
        }
        return 0;
    }
    return -1;
}

int LCC_set_tuopt(LCContext*c,int v){
    c->tuopt = v;
}

int LCC_add_argv(LCContext*c,const char*argv){
    if(!c->argv){
        c->alloc_arg = 0x10;
        c->argv = calloc(c->alloc_arg,sizeof(void*));
    }
    else if(c->argc>=c->alloc_arg){
        c->alloc_arg += 0x10;
        c->argv = realloc(c->argv,c->alloc_arg*sizeof(void*));
    }
    c->argv[c->argc++] = (char*)argv;
    return 0;
}

int LCC_set_file(LCContext *c,const char*fname){
    c->file = strdup(fname);
    //LCC_add_argv(c,c->file);
    return 0;
}

int LCC_init_clang(LCContext *c){
    c->index = clang_createIndex(0,0);

#define check_err(err) if(err!=CXError_Success) return err;
    int err = 0;
    int opt =
            CXTranslationUnit_DetailedPreprocessingRecord| //构建详细的预处理记录
            CXTranslationUnit_Incomplete |            //表示代码不完全
            CXTranslationUnit_PrecompiledPreamble|    //建立预编译头
            CXTranslationUnit_CacheCompletionResults  //开启每次缓存代码补全结果
            |CXTranslationUnit_ForSerialization       //产生预编译头?
            |CXTranslationUnit_CXXChainedPCH        //
            |CXTranslationUnit_SkipFunctionBodies   //指示方法体应该跳过解析
            |CXTranslationUnit_CreatePreambleOnFirstParse //预编译前导码
            ;
    if(c->tuopt){
        opt = c->tuopt;
    }
    //处理单元
    err = clang_parseTranslationUnit2(c->index,
                                0,
                                (const char**)c->argv,
                                c->argc,
                                0,0,
                                //CXTranslationUnit_CacheCompletionResults,
                                opt,
                                &c->TU
                                );

    check_err(err);
    err = clang_reparseTranslationUnit(c->TU,0,0,
                                       clang_defaultReparseOptions(c->TU)
                                       );
    check_err(err);
    return 0;
}

static char* getstr(CXString s){
    return s.data?(char*)s.data:"";
}

int LCC_completion_get(LCContext*c,const char*fname,int line,int col){
    if(c->last_result){
        clang_disposeCodeCompleteResults(c->last_result);
        c->last_result = 0;
    }
    CXCodeCompleteResults *r = clang_codeCompleteAt(
                c->TU,fname,line,col,
                c->unsaved_files,c->unsaved_files_num,
                1);

    if(!r)
        return -1;

//#define TEST
#ifdef TEST
    //遍历
    for(unsigned i=0;i<r->NumResults;i++){
        CXCompletionResult *ru = r->Results + i;
        int n = clang_getNumCompletionChunks(ru->CompletionString);
        for(int idx=0;idx<n;idx++){
            int kind = clang_getCompletionChunkKind(ru->CompletionString,idx);
            CXString s = clang_getCompletionChunkText(ru->CompletionString,idx);
            printf("%d %s\n",kind,getstr(s));
            clang_disposeString(s);
        }

    }
#endif
    c->last_result = r;
    return 0;
}


int LCC_completion_proc_callback(LCContext*c,const char*fname,int line,int col,void*user_data,completionProcCallBack cb){
    if(c->last_result){
        clang_disposeCodeCompleteResults(c->last_result);
        c->last_result = 0;
    }
    CXCodeCompleteResults *r = clang_codeCompleteAt(
                c->TU,fname,line,col,
                c->unsaved_files,c->unsaved_files_num,
                1);
    if(!r)
        return -1;
    //遍历
    if(cb){
        for(unsigned i=0;i<r->NumResults;i++){
            CXCompletionResult *ru = r->Results + i;
            int n = clang_getNumCompletionChunks(ru->CompletionString);
            int matchidx = -1;
            if(c->match){
                //匹配模式,查找kind = 1的值
                for(int idx=0;idx<n;idx++){
                    int kind = clang_getCompletionChunkKind(ru->CompletionString,idx);
                    if(kind==1){
                        CXString s = clang_getCompletionChunkText(ru->CompletionString,idx);
                        if(!memcmp(c->match,s.data,c->matchlen)){
                            matchidx = idx;
                        }
                        clang_disposeString(s);;
                    }
                }
            }
            if(c->match && matchidx<0){
                continue;
            }

            //0,表示开始发送单条的补全信息
            cb(user_data,0,0,"");
            for(int idx=0;idx<n;idx++){
                int kind = clang_getCompletionChunkKind(ru->CompletionString,idx);
                CXString s = clang_getCompletionChunkText(ru->CompletionString,idx);
                //回调函数
                cb(user_data,1,kind,getstr(s));
                clang_disposeString(s);
            }
            //0x10,表示结束发送信息
            cb(user_data,0x10,0,"");

        }
    }
    c->last_result = r;
    return 0;
}

int LCC_completion_proc(LCContext*c,const char*fname,int line,int col){
    return LCC_completion_get(c,fname,line,col);
}

void LCCStringList_free(LCCStringList*l){
    if(l){
        if(l->list){
            for(int i=0;i<l->len;i++){
                free(l->list[i]);
            }
            free(l->list);
        }
        free(l);
    }
}

int LCCStringList_len(LCCStringList*l){
    if(l)
        return l->len;
    return -1;
}

const char*LCCStringList_get(LCCStringList*l,int idx){
    if(l){
        if(l->list && idx<l->len){
            return l->list[idx];
        }
    }
    return 0;
}

int LCCStringList_add(LCCStringList*l,const char*s){
    if(l){
        if(!l->list)
        {
            l->allocSize = 0x100;
            l->list = calloc(1,l->allocSize*sizeof(void*));
        }
        else if(l->len>=l->allocSize){
            l->allocSize+=0x100;
            l->list = realloc(l->list,l->allocSize*sizeof(void*));
        }
        l->list[l->len++] = strdup(s);
    }
    return 0;
}

int LCC_buffer_add(LCContext*c,const char*fname,const char*buf){
    if(c->unsaved_files==0){
        c->alloc_unsaved_files = 0x10;
        c->unsaved_files = calloc(1,c->alloc_unsaved_files*sizeof(*c->unsaved_files));
    }
    else if(c->alloc_unsaved_files<=c->unsaved_files_num){
        c->alloc_unsaved_files += 0x10;
        c->unsaved_files = realloc(c->unsaved_files,c->alloc_unsaved_files*sizeof(*c->unsaved_files));
    }
    int i;
    for(i=0;i<c->unsaved_files_num;i++){
        if(!strcmp(c->unsaved_files[i].Filename,fname)){
            if(c->unsaved_files[i].Contents)
                free((char*)c->unsaved_files[i].Contents);
            c->unsaved_files[i].Contents = strdup(buf);
            c->unsaved_files[i].Length = strlen(buf);
            return i;
        }
    }
    c->unsaved_files[i].Filename = strdup(fname);
    c->unsaved_files[i].Contents = strdup(buf);
    c->unsaved_files[i].Length = strlen(buf);
    c->unsaved_files_num = i + 1;
    return i;
}

int LCC_buffer_getidx(LCContext*c,const char*name){
    int i;
    for(i=0;i<c->unsaved_files_num;i++){
        if(!strcmp(c->unsaved_files[i].Filename,name)){
            return i;
        }
    }
    return -1;
}

int LCC_buffer_removeAll(LCContext*c){
    int i;
    for(i=0;i<c->unsaved_files_num;i++){
        CXUnsavedFile*f = c->unsaved_files+i;
        if(f->Filename)
            free((char*)f->Filename);
        if(f->Contents)
            free((char*)f->Contents);
        f->Contents = 0;
        f->Filename = 0;
        f->Length = 0;
    }
    c->unsaved_files_num = 0;
    return 0;
}

int LCC_buffer_set(LCContext*c,int idx,const char*buf){
    if(idx<c->unsaved_files_num){
        CXUnsavedFile *f = c->unsaved_files+idx;
        if(f->Contents)
            free((char*)f->Contents);
        f->Contents = strdup(buf);
        f->Length = strlen(buf);
        return 0;
    }
    return -1;
}

LCCStringList* LCC_completion_filter1(LCContext*c,int filter){
    LCCStringList*ret=0;
    CXCodeCompleteResults *r = c->last_result;
    if(r){
        //遍历
        for(unsigned i=0;i<r->NumResults;i++){
            CXCompletionResult *ru = r->Results + i;
            int n = clang_getNumCompletionChunks(ru->CompletionString);
            for(int idx=0;idx<n;idx++){
                int kind = clang_getCompletionChunkKind(ru->CompletionString,idx);
                if(kind==filter){
                    CXString s = clang_getCompletionChunkText(ru->CompletionString,idx);
                    if(!ret)
                        ret = calloc(1,sizeof(*ret));
                    LCCStringList_add(ret,getstr(s));
                    //printf("%d %s\n",kind,getstr(s));
                    clang_disposeString(s);
                }
            }
        }
        return ret;
    }
    return ret;
}


void*getdata(const char*fn){
    FILE*f = fopen(fn,"rb");
    fseek(f,0,SEEK_END);
    int len = ftell(f);
    fseek(f,0,SEEK_SET);
    void*d = calloc(1,len);
    fread(d,len,1,f);
    return d;
}

#ifdef TEST
static int callback(void*userdata,int s,int kind,const char*str){
    if(s==0x10){
        printf("\n");
    }
    if(s==1)
    printf("%s ",str);
    return 0;
}

int main(int argc,char**argv){
    LCContext*c = LCC_init();
    LCC_set_file(c,"a.c");
    LCC_init_clang(c);
    //LCC_completion_get(c,"a.c",10,20);


    LCC_buffer_add(c,"a.c",getdata("b.c"));
    //LCC_completion_proc(c,"a.c",10,20);
    LCC_completion_proc_callback(c,
                                 "a.c", //源码
                                 10,20, //坐标
                                 0,     //用户数据
                                 callback //回调函数
                                 ); //
//    LCCStringList *sl = LCC_completion_filter1(c,1);

//    if(sl){
//        int l = LCCStringList_len(sl);
//        for(int i=0;i<l;i++){
//            printf("%s\n",LCCStringList_get(sl,i));
//        }
//    }
    LCC_free(c);
    return 0;
}
#endif


